﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UIObjects.TreeViewInterfaces;
using System.Collections.ObjectModel;
using TreeBuilder.Contracts;
using MvvmTreeView;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows;
using System.ServiceModel.DomainServices.Client;
using System.ComponentModel;
using System.Collections.Specialized;
using UIObjects.DragDropInterfaces;

namespace TreeBuilder.ViewModels
{
    [TreeNode(typeof(TreeBuilderTreeNodeBase), "")]
    public abstract class TreeBuilderTreeNodeBase : ITreeNode, ITreeNodeBase
    {
        protected ITreeBuilderService treeBuilderService;
        protected TreeNodeCollection nodes;
        protected ITreeView treeView;
        protected bool isExpanded;
        protected bool isChildrenLoaded;
        protected ITreeNode parent;
        protected ContextMenu contextMenu;
        protected List<string> addNewShortCuts;
        protected BitmapImage layoverImage;
        protected BitmapImage glyph;
        public event ImageAvailableHandler ImageAvailable;
        public event ChildrenLoadedHandler ChildrenLoaded;
        public event ImageAvailableHandler GlyphAvailable;
        public event ImageAvailableHandler LayoverImageAvailable;
        public virtual event PropertyChangedEventHandler PropertyChanged;
        public object TreeViewItem { get; set; }

        public TreeBuilderTreeNodeBase(ITreeBuilderService treeBuilderService, ITreeView treeView, ITreeNode parent)
        {
            this.treeView = treeView;
            this.parent = parent;
            this.treeBuilderService = treeBuilderService;

            nodes = new TreeNodeCollection();
            addNewShortCuts = new List<string>();
        }

        public abstract string Text { get; set; }
        public abstract ContextMenu ContextMenu { get; set; }
        public abstract bool? HasChildren { get; }
        public abstract string ID { get; }
        public abstract void LoadChildren();
        public abstract IEnumerable<ITreeNodeBase> Children { get; }
        public abstract void Remove();
        public abstract AbstraX.ClientInterfaces.IBase BaseObject { get; set; }
        public abstract bool AllowDrop { get; set; }
        public abstract bool AllowRename { get; set; }
        public object ItemContent { get; set; }
        public abstract ITreeNode ShallowClone();
        public abstract ITreeNode DeepClone();
        public abstract void AddNew(string shortCut);
        public abstract void DragStarting(object sender, UIObjects.DragDropInterfaces.ItemDragEventArgs e);
        public abstract void DragCompleted(object sender, UIObjects.DragDropInterfaces.ItemDragEventArgs e);
        public abstract void ItemDropped(object sender, UIObjects.DragDropInterfaces.ItemDragEventArgs e, int insertionIndex);
        public abstract void GiveFeedback(object sender, GiveFeedbackEventArgs e);
        public abstract void DragEnter(object sender, UIObjects.DragDropInterfaces.DragEventArgs e);
        public abstract void DragLeave(object sender, UIObjects.DragDropInterfaces.DragEventArgs e);
        public abstract void DragOver(object sender, UIObjects.DragDropInterfaces.DragEventArgs e);
        public abstract ITreeNodeBase InternalNodeObject { get; }
        public abstract bool HasLayoverImage<T>(out T image);
        public abstract float ChildOrdinal { get; set; }
        public ITreeNodeBase ReferenceNode { get; set; }
        public ITreeNodeBase ShortcutNode { get; set; }
        public abstract void OnSelect();

        protected void NotifyImageAvailable(ITreeNodeBase node)
        {
            if (ImageAvailable != null)
            {
                ImageAvailable(this);
            }
        }

        public List<string> AddNewShortCuts
        {
            get 
            {
                return addNewShortCuts;
            }
        }

        public bool IsReference
        {
            get
            {
                return this.ReferenceNode != null;
            }
        }

        public bool IsShortcut
        {
            get
            {
                return this.ShortcutNode != null;
            }
        }

        public void OnChildrenLoaded(ITreeNodeBase parent, EventArgs e)
        {
            parent.Children.AsQueryable().Cast<ITreeNodeBase>()
            .AsQueryable()
            .Select(r => this.CreateNode(r)).Where(t => !this.Nodes.Any(n => ((ITreeNodeServerEntity)n.Tag).ID == ((ITreeNodeServerEntity)(t.Tag)).ID))
            .OrderBy(t => t.ChildOrdinal)
            .ToList()
            .ForEach(d => this.Nodes.Add(d));

            isChildrenLoaded = true;
        }

        public bool IsChildrenLoaded
        {
            get 
            {
                return isChildrenLoaded;
            }
        }

        public void AddAdvancedQuery(object sender, RoutedEventArgs ea)
        {
            var createOperation = treeBuilderService.CreateAdvancedQueryNode(this.InternalNodeObject, this.BaseObject);

            createOperation.NodeCreated += (parent, e) =>
            {
                this.Nodes.Add(new TreeBuilderTreeNodeAdvancedQuery(treeBuilderService, treeView, createOperation.NewNode, this) { ChildOrdinal = this.Nodes.Count });
                this.Expand();
            };
        }

        public void AddQuery(object sender, RoutedEventArgs ea)
        {
            var createOperation = treeBuilderService.CreateQueryNode(this.InternalNodeObject, this.BaseObject);

            createOperation.NodeCreated += (parent, e) =>
            {
                this.Nodes.Add(new TreeBuilderTreeNodeQuery(treeBuilderService, treeView, createOperation.NewNode, this) { ChildOrdinal = this.Nodes.Count });
                this.Expand();
            };
        }

        public object BackColor
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public virtual TreeBuilderTreeNodeBase CreateNode(ITreeNodeBase treeNodeBase)
        {
            if (treeNodeBase is ITreeBuilderRoot)
            {
                return new TreeBuilderRoot(treeBuilderService, treeView, (ITreeBuilderRoot) treeNodeBase, this);
            }
            else if (treeNodeBase is ITreeNodeAdvancedQuery)
            {
                return new TreeBuilderTreeNodeAdvancedQuery(treeBuilderService, treeView, (ITreeNodeAdvancedQuery)treeNodeBase, this);
            }
            else if (treeNodeBase is ITreeNodeAttributeNode)
            {
                return new TreeBuilderTreeNodeAttributeNode(treeBuilderService, treeView, (ITreeNodeAttributeNode)treeNodeBase, this);
            }
            else if (treeNodeBase is ITreeNodeElementNode)
            {
                return new TreeBuilderTreeNodeElementNode(treeBuilderService, treeView, (ITreeNodeElementNode)treeNodeBase, this);
            }
            else if (treeNodeBase is ITreeNodeFolder)
            {
                return new TreeBuilderTreeNodeFolder(treeBuilderService, treeView, (ITreeNodeFolder)treeNodeBase, this);
            }
            else if (treeNodeBase is ITreeNodePropertyBinding)
            {
                return new TreeBuilderTreeNodePropertyBinding(treeBuilderService, treeView, (ITreeNodePropertyBinding)treeNodeBase, this);
            }
            else if (treeNodeBase is ITreeNodeTransferMapping)
            {
                return new TreeBuilderTreeNodeTransferMapping(treeBuilderService, treeView, (ITreeNodeTransferMapping)treeNodeBase, this);
            }
            else if (treeNodeBase is ITreeNodeQuery)
            {
                return new TreeBuilderTreeNodeQuery(treeBuilderService, treeView, (ITreeNodeQuery)treeNodeBase, this);
            }
            else if (treeNodeBase is ITreeNodeRootSource)
            {
                return new TreeBuilderTreeNodeRootSource(treeBuilderService, treeView, (ITreeNodeRootSource)treeNodeBase, this);
            }
            else if (treeNodeBase is ITreeNodeMenuItem)
            {
                return new TreeBuilderTreeNodeMenuItem(treeBuilderService, treeView, (ITreeNodeMenuItem)treeNodeBase, this);
            }

            throw new Exception("Invalid node creation request");
        }

        public object Bounds
        {
            get { throw new NotImplementedException(); }
        }

        public bool Checked
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public ITreeNode FirstNode
        {
            get { throw new NotImplementedException(); }
        }

        public string FullPath
        {
            get { throw new NotImplementedException(); }
        }

        public bool IsEditing
        {
            get { throw new NotImplementedException(); }
        }

        public bool IsExpanded
        {
            get 
            {
                return isExpanded;
            }
        }

        public bool IsSelected
        {
            get { throw new NotImplementedException(); }
        }

        public bool IsVisible
        {
            get { throw new NotImplementedException(); }
        }

        public ITreeNode LastNode
        {
            get { throw new NotImplementedException(); }
        }

        public int Level
        {
            get { throw new NotImplementedException(); }
        }

        public ITreeNode NextNode
        {
            get { throw new NotImplementedException(); }
        }

        public ITreeNode NextVisibleNode
        {
            get { throw new NotImplementedException(); }
        }

        [TreeNodeChildSource(typeof(TreeBuilderTreeNodeBase), DragDropEffects.Copy, "", "", "")]
        public virtual TreeNodeCollection Nodes
        {
            get 
            {
                if (!this.IsChildrenLoaded)
                {
                    this.LoadChildren();
                }

                return nodes;
            }
        }

        public ITreeNode Parent
        {
            get 
            {
                return parent;
            }
        }

        public ITreeNode PrevNode
        {
            get { throw new NotImplementedException(); }
        }

        public ITreeNode PrevVisibleNode
        {
            get { throw new NotImplementedException(); }
        }

        public int SelectedImageIndex { get; set; }
        public string SelectedImageKey { get; set; }
        public int StateImageIndex { get; set; }
        public string StateImageKey { get; set; }
        public object Tag { get; set; }
        public int ImageIndex { get; set; }
        public string ImageKey { get; set; }

        public string ToolTipText
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public ITreeView TreeView
        {
            get 
            {
                return treeView;
            }
        }

        public void BeginEdit()
        {
            throw new NotImplementedException();
        }

        public object Clone()
        {
            throw new NotImplementedException();
        }

        public void Collapse()
        {
            throw new NotImplementedException();
        }

        public void Collapse(bool ignoreChildren)
        {
            throw new NotImplementedException();
        }

        public void EndEdit(bool cancel)
        {
            throw new NotImplementedException();
        }

        public void EnsureVisible()
        {
            throw new NotImplementedException();
        }

        public virtual void Expand()
        {
            if (this.TreeViewItem != null)
            {
                ((TreeViewItem) this.TreeViewItem).IsExpanded = true;
                this.isExpanded = true;
            }
        }

        public virtual void ExpandAll()
        {
        }

        public int GetNodeCount(bool includeSubTrees)
        {
            throw new NotImplementedException();
        }

        public void Toggle()
        {
            throw new NotImplementedException();
        }

        public object Image
        {
            get
            {
                BitmapImage image = null;
                object tagObject = this.Tag;

                if (tagObject != null)
                {
                    var key = tagObject.ToString();

                    if (treeView.ImageList.Images.ContainsKey(key))
                    {
                        image = treeView.ImageList.Images[key];
                    }
                }

                return image;
            }
        }

        public virtual void StartLoadImage()
        {
            var tagObject = this.Tag;

            if (tagObject != null)
            {
                var key = tagObject.ToString();
                var imageList = treeView.ImageList;

                if (imageList.Images.ContainsKey(key))
                {
                    ImageAvailable(this);
                }
                else
                {
                    if (imageList.ImageLoadHandlers.ContainsKey(key))
                    {
                        ImageAvailable(this);
                    }
                    else
                    {
                        var image = treeBuilderService.LoadImageForItemType(key);

                        imageList.Images.Add(key, image, Colors.Transparent);

                        ImageAvailable(this);
                    }
                }
            }

            if (this.HasLayoverImage(out layoverImage))
            {
                LayoverImageAvailable(this);
            }
        }

        public void NoChildren()
        {
            ChildrenLoaded(this, EventArgs.Empty);
        }

        public object Glyph
        {
            get 
            {
                return glyph;
            }
        }

        public object LayoverImage
        {
            get 
            {
                return layoverImage;    
            }
        }

        public virtual string SelectionStatus
        {
            get
            {
                return null;
            }
        }
    }
}
